import simpy
import random

# 1. 随即数种子保证每次产生结果都一样
# 2. 医生看病耗时是指数分布 lambda=3
# 3. 模拟进入就诊队列的时间
# 4. 模拟看医生时间
# 5. 模拟离开医生时间
# 6. 记录进入就诊队列时间、看医生时间、离开医生时间
# 7. 模拟65%的回诊概率
# 8. 模拟回诊后优先插队


RANDOM_SEED = 42
random.seed(RANDOM_SEED)

see_doctor_twice_probability = 0.65
see_doctor_duration_expectation = 3
laboratory_duration_expectation = 4

class Outpatient:

    def __init__(self, env: simpy.Environment, id: int, doctor: simpy.PriorityResource, laboratory: simpy.Resource):
        self.env = env
        self.id = id
        self.name = f"P_{id}"
        self.doctor = doctor
        self.laboratory = laboratory
        self.after_registry_time = None
        self.before_see_doctor_once_time = None
        self.after_see_doctor_once_time = None
        self.before_see_doctor_twice_time = None
        self.after_see_doctor_twice_time = None
        self.before_laboratory_time = None
        self.after_laboratory_time = None

    def process(self):
        self.after_registry_time = self.env.now
        print(f'患者 {self.name} 挂号于 {self.after_registry_time}， 进入就诊队列...')
        with self.doctor.request() as see_doctor_once_request:
            yield see_doctor_once_request
            see_doctor_once_duration = random.expovariate(see_doctor_duration_expectation)
            self.before_see_doctor_once_time = self.env.now
            see_doctor_once_queue_duration = self.before_see_doctor_once_time - self.after_registry_time
            print(f'患者: {self.name} 首次看医生于: {self.before_see_doctor_once_time}， 排队耗时: {see_doctor_once_queue_duration}')
            yield self.env.timeout(see_doctor_once_duration)
            self.after_see_doctor_once_time = self.env.now
            print(f'患者: {self.name} 首次离开医生于: {self.after_see_doctor_once_time}, 就诊耗时: {see_doctor_once_duration}')
        if random.random() > 0.65:
            print(f'患者: {self.name} 进入回诊流程, 排队检查检验...')
            with self.laboratory.request() as laboratory_request:
                yield laboratory_request
                self.before_laboratory_time = env.now
                laboratory_duration = random.expovariate(laboratory_duration_expectation)
                laboratory_queue_time = self.before_laboratory_time - self.after_see_doctor_once_time
                print(f'患者: {self.name} 检查检验于: {self.before_laboratory_time} 排队耗时: {laboratory_queue_time}')
                yield self.env.timeout(laboratory_duration)
                self.after_laboratory_time = self.env.now
                print(f'患者: {self.name} 结束检查检验于: {self.after_laboratory_time}, 检查检验耗时: {laboratory_duration}')
                with self.doctor.request() as see_doctor_twice_request:
                    yield see_doctor_twice_request
                    see_doctor_twice_duration = random.expovariate(see_doctor_duration_expectation)
                    self.before_see_doctor_twice_time = self.env.now
                    see_doctor_twice_queue_duration = self.before_see_doctor_twice_time - self.after_laboratory_time
                    print(f'患者: {self.name} 第二次看医生于: {self.before_see_doctor_twice_time}， 排队耗时: {see_doctor_twice_queue_duration}')
                    yield self.env.timeout(see_doctor_twice_duration)
                    self.after_see_doctor_twice_time = self.env.now
                    print(f'患者: {self.name} 第二次离开医生于: {self.after_see_doctor_twice_time}， 就诊耗时: {see_doctor_twice_duration}')

    def __repr__(self):
        return f"{self.name}, {self.after_registry_time}, {self.before_see_doctor_once_time}, {self.after_see_doctor_once_time}, {self.before_laboratory_time}, {self.after_laboratory_time}, {self.before_see_doctor_twice_time}, {self.after_see_doctor_twice_time}"

env = simpy.Environment()
doctor = simpy.PriorityResource(env, capacity=1)
laboratory = simpy.Resource(env, capacity=1)

outpatients = []
for id in range(0, 30):
    outpatient = Outpatient(env, id, doctor, laboratory)
    env.process(outpatient.process())
    outpatients.append(outpatient)

env.run(until=20)

for outpatient in outpatients:
    print(outpatient)
